package tcp.server;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@SuppressWarnings({"all"})
public class TcpEchoServer {
    ServerSocket serverSocket = null;
    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }
    public void start() throws IOException {
        ExecutorService service = Executors.newCachedThreadPool();// 此处不太适合使用 "固定个数的"
        while (true) {
            // 服务端中接收到客户端建立连接（accept方法）的请求后，返回的服务端Socket
            // 如果当前没有客户端来建立连接, 就会阻塞等待!!
            Socket clientSocket = serverSocket.accept();
            // processConnection(clientSocket); // 单线程模式——即该服务器无法同时为多个客户端同时提供服务

            // [版本2] 多线程版本. 主线程负责拉客, 新线程负责通信
            // 虽然比版本1 有提升了, 但是涉及到频繁创建销毁线程, 在高并发的情况下, 负担比较重的.
//            Thread thread = new Thread(() -> {
//                try {
//                    processConnection(clientSocket);
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            });
//            thread.start();

            // [版本3] 使用线程池, 来解决频繁创建销毁线程的问题.
            // 此处不太适合使用 "固定个数的"
            service.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnection(clientSocket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });

        }

    }
    // 通过这个方法, 给当前连上的这个客户端, 提供服务!!
    // 这里我们建立长连接——一次连接中，会有 N次请求，N次响应
    public void processConnection(Socket clientSocket) throws IOException {
        System.out.printf("连接成功！[%s:%d]\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream() ) {
            Scanner scannerNet = new Scanner(inputStream); // 对我们的读取进行嵌套
            PrintWriter printWriter = new PrintWriter(outputStream);
            while (true) {
                if (!scannerNet.hasNext()) {
                    // 说明此时连接中断，服务器无法继续从客户端读取到请求
                    // 连接断开. 当客户端断开连接的时候, 此时 hasNext 就会返回 false
                    System.out.printf("[%s:%d] 断开连接!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
                    break;
                }
                // 1、读取客户端的请求
                String request = scannerNet.nextLine();
                // 从客户端读取到的请求，如果客户端在输入请求的时候，只是通过nextLine敲了回车，然后就通过printWrite.write发送给我们的服务器，
                // 这时服务器接收到的数据是不带换行符的，换行符在我们的客户端nextLine输入的过程中就已经被吞吃了，所有为了避免String request = scanner.nextLine();无法正常读取（nextLine只有遇到换行才结束)
                // 所以在我们的客户端在发送时要通过PrintWrite.println()来发送，会给我们自动添加一个换行符
                // 2、处理客户端发来的请求
                String response = process(request);
                // 3、把响应发送给客户端
                printWriter.println(response);
                // printWriter.write(response); // 这样写有bug，因为我们当前的response中没有带换行符，
                printWriter.flush(); // 刷新缓冲区
                // 打印日志
                System.out.printf("[%s:%d]  request: %s, response: %s\n",
                        clientSocket.getInetAddress(), clientSocket.getPort(),
                        request, response);
            }
        }
        finally {
            clientSocket.close(); // 每一次建立连接都会创建一个clientSocket，该连接结束后要及时关闭，避免内存泄漏
        }

    }
    public String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer server = new TcpEchoServer(8000);
        server.start();
    }
}
